{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "数据并行（选读）\n",
    "==========================\n",
    "**Authors**: [Sung Kim](https://github.com/hunkim) and [Jenny Kang](https://github.com/jennykang)\n",
    "\n",
    "在这个教程里，我们将学习如何使用 ``DataParallel`` 来使用多GPU。 \n",
    "\n",
    "PyTorch非常容易就可以使用多GPU，用如下方式把一个模型放到GPU上：\n",
    "\n",
    "```python\n",
    "\n",
    "    device = torch.device(\"cuda:0\")\n",
    "    model.to(device)\n",
    "```\n",
    " GPU:\n",
    "然后复制所有的张量到GPU上：\n",
    "```python\n",
    "\n",
    "    mytensor = my_tensor.to(device)\n",
    "```\n",
    "请注意，只调用``my_tensor.to(device)``并没有复制张量到GPU上，而是返回了一个copy。所以你需要把它赋值给一个新的张量并在GPU上使用这个张量。\n",
    "\n",
    "在多GPU上执行前向和反向传播是自然而然的事。\n",
    "但是PyTorch默认将只使用一个GPU。\n",
    "\n",
    "使用``DataParallel``可以轻易的让模型并行运行在多个GPU上。\n",
    "\n",
    "\n",
    "```python\n",
    "\n",
    "    model = nn.DataParallel(model)\n",
    "```\n",
    "这才是这篇教程的核心，接下来我们将更详细的介绍它。\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "导入和参数\n",
    "----------------------\n",
    "\n",
    "导入PyTorch模块和定义参数。\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import torch.nn as nn\n",
    "from torch.utils.data import Dataset, DataLoader\n",
    "\n",
    "# Parameters and DataLoaders\n",
    "input_size = 5\n",
    "output_size = 2\n",
    "\n",
    "batch_size = 30\n",
    "data_size = 100"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Device\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "虚拟数据集\n",
    "-------------\n",
    "\n",
    "制作一个虚拟（随机）数据集，\n",
    "你只需实现 `__getitem__`\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "class RandomDataset(Dataset):\n",
    "\n",
    "    def __init__(self, size, length):\n",
    "        self.len = length\n",
    "        self.data = torch.randn(length, size)\n",
    "\n",
    "    def __getitem__(self, index):\n",
    "        return self.data[index]\n",
    "\n",
    "    def __len__(self):\n",
    "        return self.len\n",
    "\n",
    "rand_loader = DataLoader(dataset=RandomDataset(input_size, data_size),\n",
    "                         batch_size=batch_size, shuffle=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "简单模型\n",
    "------------\n",
    "作为演示，我们的模型只接受一个输入，执行一个线性操作，然后得到结果。\n",
    "说明：``DataParallel``能在任何模型（CNN，RNN，Capsule Net等）上使用。\n",
    "\n",
    "\n",
    "我们在模型内部放置了一条打印语句来打印输入和输出向量的大小。\n",
    "\n",
    "请注意批次的秩为0时打印的内容。\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Model(nn.Module):\n",
    "    # Our model\n",
    "\n",
    "    def __init__(self, input_size, output_size):\n",
    "        super(Model, self).__init__()\n",
    "        self.fc = nn.Linear(input_size, output_size)\n",
    "\n",
    "    def forward(self, input):\n",
    "        output = self.fc(input)\n",
    "        print(\"\\tIn Model: input size\", input.size(),\n",
    "              \"output size\", output.size())\n",
    "\n",
    "        return output"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "创建一个模型和数据并行\n",
    "-----------------------------\n",
    "\n",
    "这是本教程的核心部分。\n",
    "\n",
    "首先，我们需要创建一个模型实例和检测我们是否有多个GPU。\n",
    "如果有多个GPU，使用``nn.DataParallel``来包装我们的模型。\n",
    "然后通过``model.to(device)``把模型放到GPU上。\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Model(\n",
       "  (fc): Linear(in_features=5, out_features=2, bias=True)\n",
       ")"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model = Model(input_size, output_size)\n",
    "if torch.cuda.device_count() > 1:\n",
    "  print(\"Let's use\", torch.cuda.device_count(), \"GPUs!\")\n",
    "  # dim = 0 [30, xxx] -> [10, ...], [10, ...], [10, ...] on 3 GPUs\n",
    "  model = nn.DataParallel(model)\n",
    "\n",
    "model.to(device)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "运行模型\n",
    "-------------\n",
    "\n",
    "现在可以看到输入和输出张量的大小。\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\tIn Model: input size torch.Size([30, 5]) output size torch.Size([30, 2])\n",
      "Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])\n",
      "\tIn Model: input size torch.Size([30, 5]) output size torch.Size([30, 2])\n",
      "Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])\n",
      "\tIn Model: input size torch.Size([30, 5]) output size torch.Size([30, 2])\n",
      "Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])\n",
      "\tIn Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])\n",
      "Outside: input size torch.Size([10, 5]) output_size torch.Size([10, 2])\n"
     ]
    }
   ],
   "source": [
    "for data in rand_loader:\n",
    "    input = data.to(device)\n",
    "    output = model(input)\n",
    "    print(\"Outside: input size\", input.size(),\n",
    "          \"output_size\", output.size())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "结果\n",
    "-------\n",
    "\n",
    "当没有或者只有一个GPU时，对30个输入和输出进行批处理，得到了期望的一样得到30个输入和输出，但是如果你有多个GPU，你得到如下的结果。\n",
    "\n",
    "\n",
    "2 GPUs\n",
    "~\n",
    "\n",
    "If you have 2, you will see:\n",
    "\n",
    ".. code:: bash\n",
    "\n",
    "    # on 2 GPUs\n",
    "    Let's use 2 GPUs!\n",
    "        In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])\n",
    "        In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])\n",
    "    Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])\n",
    "        In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])\n",
    "        In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])\n",
    "    Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])\n",
    "        In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])\n",
    "        In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])\n",
    "    Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])\n",
    "        In Model: input size torch.Size([5, 5]) output size torch.Size([5, 2])\n",
    "        In Model: input size torch.Size([5, 5]) output size torch.Size([5, 2])\n",
    "    Outside: input size torch.Size([10, 5]) output_size torch.Size([10, 2])\n",
    "\n",
    "3 GPUs\n",
    "~\n",
    "\n",
    "If you have 3 GPUs, you will see:\n",
    "\n",
    ".. code:: bash\n",
    "\n",
    "    Let's use 3 GPUs!\n",
    "        In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])\n",
    "        In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])\n",
    "        In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])\n",
    "    Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])\n",
    "        In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])\n",
    "        In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])\n",
    "        In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])\n",
    "    Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])\n",
    "        In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])\n",
    "        In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])\n",
    "        In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])\n",
    "    Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])\n",
    "        In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])\n",
    "        In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])\n",
    "        In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])\n",
    "    Outside: input size torch.Size([10, 5]) output_size torch.Size([10, 2])\n",
    "\n",
    "8 GPUs\n",
    "~~\n",
    "\n",
    "If you have 8, you will see:\n",
    "\n",
    ".. code:: bash\n",
    "\n",
    "    Let's use 8 GPUs!\n",
    "        In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])\n",
    "        In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])\n",
    "        In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])\n",
    "        In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])\n",
    "        In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])\n",
    "        In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])\n",
    "        In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])\n",
    "        In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])\n",
    "    Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])\n",
    "        In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])\n",
    "        In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])\n",
    "        In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])\n",
    "        In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])\n",
    "        In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])\n",
    "        In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])\n",
    "        In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])\n",
    "        In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])\n",
    "    Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])\n",
    "        In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])\n",
    "        In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])\n",
    "        In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])\n",
    "        In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])\n",
    "        In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])\n",
    "        In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])\n",
    "        In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])\n",
    "        In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])\n",
    "    Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])\n",
    "        In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])\n",
    "        In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])\n",
    "        In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])\n",
    "        In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])\n",
    "        In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])\n",
    "    Outside: input size torch.Size([10, 5]) output_size torch.Size([10, 2])\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "总结\n",
    "-------\n",
    "\n",
    "DataParallel会自动的划分数据，并将作业发送到多个GPU上的多个模型。\n",
    "并在每个模型完成作业后，收集合并结果并返回。\n",
    "\n",
    "更多信息请看这里：\n",
    "https://pytorch.org/tutorials/beginner/former\\_torchies/parallelism\\_tutorial.html.\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Pytorch for Deeplearning",
   "language": "python",
   "name": "pytorch"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
